package model;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;

public class WavToArffParser extends WavParser {

	private static PrintWriter writer = null;

	/**
	 * 
	 * @param absoluteFileName
	 *            - directory name or filename with files to parse (absolute)
	 * @param absoluteResultName
	 *            - path to the result file
	 * @param isClassified
	 *            - if is true, it's a training set, test set otherwise
	 * 
	 * @param isCustomExtractor
	 *            - set to true means that you want to
	 * 
	 * @param attributesCountX
	 *            - how many coefficients have to be attributes in case
	 *            customExtractor was chosen; it takes X first and X last coeffs
	 * @return
	 */
	public static Instances parseToArff(String absoluteFileName,
			String absoluteResultName, boolean isClassified,
			boolean isCustomExtractor, Integer attributesCountX) {

		File[] fToParse = getFiles(absoluteFileName, WAV_EXTENSION);

		Map<String, double[][]> coeffsMap = new HashMap<String, double[][]>();
		Set<String> classes = new HashSet<String>();
		int attributesCountMax = 0;
		int attributesCountMin = Integer.MAX_VALUE;
		for (File file : fToParse) {
			double[][] coeffs = getCoefficients(file.getAbsolutePath());
			coeffsMap.put(file.getName(), coeffs);
			attributesCountMax = Math.max(attributesCountMax, coeffs.length);
			attributesCountMin = Math.min(attributesCountMin, coeffs.length);

			int idx = file.getName().indexOf("_");
			/*
			 * if it is custom extractor, we choose first and last letter as
			 * classes
			 */
			if (isCustomExtractor) {
				classes.add(file.getName().substring(0, 1));
				//classes.add(file.getName().substring(idx - 1, idx));
			} else {
				classes.add(file.getName().substring(0, idx));
			}
		}

		if (isCustomExtractor) {
			attributesCountX = attributesCountX != null ? attributesCountX
					: attributesCountMin / 2;
		} else {
			attributesCountX = attributesCountMax;
		}

		parseToArff(coeffsMap, attributesCountX, absoluteResultName,
				isClassified, classes, isCustomExtractor);

		Instances instances = getInstances(absoluteResultName);
		return instances;
	}

	private static Instances getInstances(String arffAbsoluteFileName) {
		DataSource source = null;
		Instances instances = null;
		try {
			source = new DataSource(arffAbsoluteFileName);
			instances = source.getDataSet();
			if (instances.classIndex() == -1)
				instances.setClassIndex(instances.numAttributes() - 1);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return instances;
	}

	private static void parseToArff(Map<String, double[][]> coeffsMap,
			int attributesCountX, String absoluteResultName,
			boolean isClassified, Set<String> classes, boolean isCustomExtractor) {

		writer = null;
		try {
			writer = new PrintWriter(absoluteResultName, "UTF-8");
			writer.println("@RELATION letters");

			writeAttributes(attributesCountX);
			writeClasses(classes);
			writeData(attributesCountX, coeffsMap, isClassified,
					isCustomExtractor);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} finally {
			writer.close();
		}
		return;
	}

	private static void writeAttributes(int attributesCountX) {
		for (int i = 0; i < attributesCountX; i++) {
			for (int j = 0; j < attributesCountY; j++) {
				writer.println("@ATTRIBUTE " + i + "_" + j + " NUMERIC");
			}
		}
		return;
	}

	private static void writeClasses(Set<String> classes) {
		String classAttr = "@ATTRIBUTE class {";
		int c = 0;
		int size = classes.size();
		for (String cl : classes) {
			if (c < size - 1) {
				classAttr += cl + ",";
			} else {
				classAttr += cl + "}";
			}
			c++;
		}

		writer.println(classAttr);
		return;
	}

	private static void writeData(int attributesCountX,
			Map<String, double[][]> coeffsMap, boolean isClassified,
			boolean isCustomExtractor) {
		if (isCustomExtractor) {
			writeDataFromStart(attributesCountX, coeffsMap, isClassified,
					isCustomExtractor);
		//	writeDataFromEnd(attributesCountX, coeffsMap, isClassified,
		//			isCustomExtractor);
		} else {
			writeDataFromStart(attributesCountX, coeffsMap, isClassified,
					isCustomExtractor);
		}

		return;
	}

	@SuppressWarnings("unused")
	private static void writeDataFromEnd(int attributesCountX,
			Map<String, double[][]> coeffsMap, boolean isClassified,
			boolean isCustomExtractor) {
		// writer.println("@DATA");
		for (Map.Entry<String, double[][]> coeff : coeffsMap.entrySet()) {
			int length = coeff.getValue().length;
			for (int i = 0; i < attributesCountX; i++) {
				for (int j = 0; j < attributesCountY; j++) {
					if (i != 0 || j != 0) {
						writer.print(",");
					}
					if (length - attributesCountX + i >= 0) {
						writer.print(coeff.getValue()[length - attributesCountX
								+ i][j]);
					} else {
						writer.print(MISSING_VALUE);
					}
				}
			}
			if (isClassified) {
				int idx = coeff.getKey().indexOf("_");
				String clazz = coeff.getKey().substring(0, idx);
				if (isCustomExtractor) {
					clazz = coeff.getKey().substring(idx - 1, idx);
				}
				writer.println("," + clazz);
			}
		}
		return;
	}

	private static void writeDataFromStart(int attributesCountX,
			Map<String, double[][]> coeffsMap, boolean isClassified,
			boolean isCustomExtractor) {
		writer.println("@DATA");
		for (Map.Entry<String, double[][]> coeff : coeffsMap.entrySet()) {
			for (int i = 0; i < attributesCountX; i++) {
				for (int j = 0; j < attributesCountY; j++) {
					if (i != 0 || j != 0) {
						writer.print(",");
					}
					if (i < coeff.getValue().length) {
						writer.print(coeff.getValue()[i][j]);
					} else {
						writer.print("0");
					}
				}
			}
			if (isClassified) {
				int idx = coeff.getKey().indexOf("_");
				String clazz = coeff.getKey().substring(0, idx);
				if (isCustomExtractor) {
					clazz = coeff.getKey().substring(0, 1);
				}
				writer.println("," + clazz);
			}
		}
		return;
	}

}
